嗨~大家好,我是Rafael,從原本的農業領域轉職剛滿1年,目前在AI數據公司擔任前端工程師。
最近自己籌組的Vue3線上讀書會順利結束了🥳,也剛好是以Vue3開發快近1年時間,想說整理自己撰寫的Vue讀書會綱要,並將各主題再深入探討撰寫成文章,會想發起這個心願主要一直心中有個原因:
Vue的官網其實對使用者滿好上手的,正因為如此,許多開發上細節或和正確觀念我們不得而知。也就造成許多初學開發者(包括本人),知其然而不知其所以然,亂用產生bug而不得其解的情況出現。
大概會以官網一些基本介紹為基底,加入額外文章資源,也會嘗試加入一些SOLID設計準則,希望形成一些火花看看,第一次參加鐵人賽,也希望大家閱讀時能夠給予回饋,教學相長。
30天內容,其中會不時穿插一些JavaScript當作複習並和Vue的相關主題作連結:
天數 | 主題名稱 | 摘要 |
---|---|---|
1 | Vue Create App and Mount | 認識Vue的編譯和運行核心模組 |
2 | Vue SFC樣板和渲染函式 | 渲染函式和虛擬DOM之間的關係 |
3 | script setup語法糖的本質 | <script setup> 解決Vue的那些問題? |
4 | JavaScript常見的物件操作方式 | JavaScript物件如何創建、定義屬性和複製? |
5 | JavaScript代理物件和Reactive | Vue響應式基礎-Reactive 和 Proxy 的關係 |
6 | 響應式系統-Ref | ref 使用時為什麼總是要加.value? |
7 | 計算屬性-Computed | computed 的設計核心思想和基本特性 |
8 | Vue的監聽器-watch和watchEffect | 理解Vue監聽器基本使用- 深層監聽設定等 |
9 | Vue監聽器的執行時機點 | Vue 監聽器執行函式副作用的時機點 |
10 | JavaScript事件循環、宏任務和微任務 | 理解事件循環(Event Loop)核心觀念 |
11 | Vue的nextTick-DOM更新後的回調 | 理解nextTick 的作用和執行時機點 |
12 | Vue 監聽器副作用的清除 | 認識watch 回調函式中 onCleanUp 的功能 |
13 | Vue元件的事件emits和參數props傳遞 | Vue - Props down, events up 原則 |
14 | Vue元件props和響應式資料的驗證 | defineProps 驗證功能和製作型別檢查器 |
15 | Vue-插槽(slot)的認識 | slot 插槽認識、元件 render scope 觀念 |
16 | 無渲染元件-slot props另一種用法 | 理解無渲染元件和實作 |
17 | v-for指令渲染和使用上的陷阱 | v-for 使用注意事項 |
18 | JavaScript工廠函式 vs 類別認識 | 認識 JavaScript 類別建構子和工廠函式的差別 |
19 | Vue-組合式函式(composable) | 理解Composable和一般JavaScript通用函式差別 |
20 | 依賴注入模式-Provide and Inject | 理解Vue provide inject 和正確使用模式 |
21 | SOLID-單一職責(SRP) | 認識單一職責和實作 |
22 | SOLID-開放封閉原則(OCP) | 認識開放封閉原則和實作 |
23 | SOLID-里氏替換原則(LSP) | 認識替換原則和實作 |
24 | SOLID-介面分離原則(ISP) | 認識介面分離原則和實作 |
25 | SOLID-依賴反轉原則(DIP) | 認識依賴反轉原則和實作 |
26 | JavaScript和Vue的元件錯誤處理 | try/catch/finally 語法和Vue的元件錯誤捕捉 |
27 | JavaScript模組(module)和程式碼分割 | 理解認識JavaScript模組(module) 和 動態載入 |
28 | Vue 的元件更新優化-重新渲染問題 | 理解 props stable 概念和重新渲染問題 |
29 | Vue 的不同渲染模式-CSR、SSR和通用渲染模式 | 理解不同渲染模式、SSR初步注意事項 |
30 | 總結-回顧30天的學習心路歷程 | 總結學習旅程和如何突破困難 |
那麼就開始Vue的自我探索旅程囉~Go
在我們安裝完vue後,打開main.js
檔案中應該會出現以下段落程式碼:
import { createApp } from 'vue'
// import the root component App from a single-file component.
import App from './App.vue'
const app = createApp(App)
app.mount('#app')
Vue 其實是一個大型應用專案框架包(package),裡面包含很多應用模組(module), createApp 是 Vue中的一個模組功能,引入後呼叫它並掛載入App.vue這個頂層的vue組件。
createApp 是用來創建 Vue 應用實例的 API。透過 createApp 可以生成一個新的 Vue 應用實例,並掛載根組件(通常是 App.vue),作為應用的入口點。這樣 Vue 就可以開始追蹤資料狀態變化並自動更新畫面。
一個Vue專案結構如下,我們在component
資料夾底下,可包含更多Vue子元件進行開發形成下列結構:
App (root component)
├─ TodoList
│ └─ TodoItem
│ ├─ TodoDeleteButton
│ └─ TodoEditButton
└─ TodoFooter
├─ TodoClearButton
└─ TodoStatistics
mount
顧名思義掛載的意思,當然其中有一些非原生JS語言的檔案.vue需要編譯細節不在此階段討論,不過mount
功能主要是負責將Vue.js所產生的JS程式碼,調用瀏覽器原生API(例如 document.querySelector、appendChild 等)去掛載或更新真正的DOM元素,達成瀏覽器畫面更新。
在Vue專案中,應該會有一份index.html
,裡面是實際瀏覽器運行時需要的html內容,在中通常會以id=app的元素為容器,讓Vue可以知道要截取哪一段html元素。
app.mount()
,括號中參數可以是一個 DOM 元素或一个 CSS 選擇器,傳入後Vue會一併返回成為根组件實例的一部分。
瀏覽器上運行Vue 的核心代碼(run-time code)時:
Vue 會將剛剛傳入的容器元素(例如 id="app" 的 )的 innerHTML 作為模板。Vue 會透過 JavaScript 解析器解析這些模板,將它們編譯為虛擬 DOM(Virtual DOM),然後將虛擬 DOM 渲染為實際的 DOM 元素。
最後,Vue 將模板中的內容替換為實際的組件內容,並完成最終的畫面更新。
// index.html
<div id="app"></div>
// main.js
app.mount('#app')
一個Vue app實例,僅能調用一次mount方法
掛載vue實例後vue還需要負責管理组件的生命周期、狀態和響應數據更新。如果多次調用 mount的可能造成不可預期的交互混亂 。
大意是說你不能讓一個Vue instance 掛載多次,因為後續會牽涉到資料流問題,一個實例裡包含許多彼此間不相關的資料狀態,總是有互相影響風險存在。
多頁式應用(Multiple Page Application. MPA)
雖然說Vue 主要是以單頁式應用(Single Page Aplication,SPA)
作為主要應用,但並不是說不能做成多頁式應用(Mutiple Page Aplication,MPA)
,只是需要利用createApp
創建多個Vue實例,如同官方所建議的。
MPA專案資料夾
project-root/
├─ public/
│ ├─ index.html
│ ├─ about.html
├─ src/
│ ├─ main.js // 對應 index.html 的入口文件
│ ├─ about.js // 對應 about.html 的入口文件
├─ vite.config.js
在不同檔案中創建不同的Vue App 實例
// main.js
const app1 = createApp({
/* ... */
})
app1.mount('#container-1')
// about.js
const app2 = createApp({
/* ... */
})
app2.mount('#container-2')
在Vite打包入口檔的設定,可以參考roll up文件
https://cn.rollupjs.org/configuration-options/#input
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
build: {
rollupOptions: {
input: {
main: resolve(__dirname, 'public/index.html'),
about: resolve(__dirname, 'public/about.html')
}
}
}
});
剛剛有稍微提到run-time執行核心,有時候compiler 、 run-time這兩個專有名詞還會掠過耳邊,如果你是剛接觸Vue對它的功能不是很熟悉也沒關係,先理解核心套件會負責那些項目就行,用到再回顧也行。
那麼對vue來說compiler和runtime core這兩個核心主要負責什麼呢?
在使用上最大的區別:
運行器核心 (Runtime Core):
Runtime Core 是 Vue.js 在瀏覽器中運行的核心部分,其檔案體積較小,專注於運行時的功能。通常指的是通過 Vue CLI 或 Vite 打包後的 JavaScript 檔案,這些檔案在瀏覽器中執行。當我們在網頁上與 Vue 應用互動、觸發資料更新時,Runtime Core 會負責響應式系統、組件渲染和虛擬 DOM 的更新操作,從而實現資料和畫面同步更新。
整理一下對應的Vue功能:
註解:更有效率的DOM操作不代表框架使用遠比使用原生JS DOM操作來的便宜,端看應用規模,Virtual DOM建立和運算也需耗費資源,取決於商業邏輯複雜度相較之下值不值得。
編譯器核心通常主要負責將
模板Vue template
編譯成為JS渲染函數render function
,可以想像成Vue開發完成後最終需要變成可以執行的第一份JS 程式碼。
我們通常不會在瀏覽器的運行環境中執行模板編譯,因為編譯器檔案體積較大,且在瀏覽器中進行編譯會影響性能。相反地,編譯通常發生在開發或打包過程中。
在開發時環境下我們通常使用 npm run dev(Vite)啟動開發伺服器,本地代碼修改時會觸發熱重載模式(HMR),這會自動重新編譯模板並生成新的 JavaScript 代碼。
而在生產環境中,我們通過執行 npm run build 來打包應用,將模板預編譯為渲染函數,最終生成一份優化過的 JavaScript 文件供瀏覽器運行。
整理一下對應的Vue功能:
CSS 預處理的過程並不是 Vue 編譯器的職責,而是由構建工具(如 Webpack、Vite)和對應的 Loader(如 sass-loader、less-loader 等)來處理的。
Vue 編譯器主要處理的是 Vue 特有的模板語法,而 CSS 預處理、JavaScript 模組打包等則由構建工具(如 Webpack 或 Vite)負責。構建工具通過配置不同的 Loader 或 Plugin 來處理各種資源文件的編譯和打包。
Webpack 中的 Vue Loader:需要配置 Vue Loader 並安裝相關插件,特別是在使用 CSS 預處理器時,需要相應的 Loader 配置。
Vite 的 Vue 支持:Vite 已經內建對 Vue 的支持,只需簡單配置插件即可。
// vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()]
});
其實vue源碼專案上也有他開發團隊對Vue的簡介,是採用monorepo形式去開發vue框架的,可以試著打開vue的github稍微理一下,是以上面的runtime core/compiler core 對應職責去做開發和分類。
https://github.com/vuejs/core/blob/v3.0.0-alpha.4/.github/contributing.md#project-structure
挑了下面這張圖做本次ending,整個Vue學習也是環繞著它,之後也會常常出現。 createApp後到app.mount這中間過程,其實還夾著這張圖一大段流程。
官方是擺在進階的渲染機制(Rendering Mechanism)做介紹,不過本次章節已經有觸碰到App.vue元件檔(template)的掛入,到app.mount -> Actual DOM的一些介紹,算是個開頭,後續進入元件化開發(SFC file component)、響應式資料和虛擬DOM(Virtual DOM)研究囉,希望自己還活著繼續撰寫😆。
https://cn.vuejs.org/api/application.html
https://cn.vuejs.org/guide/essentials/application.html
https://medium.com/glovo-engineering/dissecting-vue-3-the-mounting-process-i-32181abf5cc3
(這篇作者很棒,受到他的啟發才有今天的文章整理)